Goal-based agents for which states of the environment are considered as atomic representations, ones with no visible internal structure.
Formulating the search problem. We assume
Problem definition:
Definition of state set is critical. Want enough detail to enable discovery of useful solution, but want minimal detail to ensure a practical search.
Consider the sliding tile 8-puzzle. Example of
How would you write the problem definition for our simple graph?
How would you write the problem definition for the 8 or 15 puzzle?
How would you write the problem definition for the Towers of Hanoi puzzle?
How would you write the problem definition for the Peg Board Puzzle?
How would you write the problem definition for scheduling 10 different observations using the Hubble Space Telescope?
Uninformed search means that the choice of action is not "informed" by any knowledge of the goal.
Breadth-first search completely explores each level of the search space before proceeding to the next.
Depth-first search completely explores a path until it ends, then backs up a level and tries again.
from IPython.display import IFrame
IFrame("https://www.cs.colostate.edu/~anderson/cs440/notebooks/simplegraphsteps.pdf", width=800, height=600)
This example does not show the reduced space requirements of depth-first search. If the solution path was via the second child of 'a', then we would see that the nodes through the first child of 'a' do not need to be saved.
Here is an algorithm definition for both breadth-first and depth-first
search, tailored to fit the python implementation you must complete
for
Assignment 1. The
algorithm maintains a local variable named un_expanded
to be a list
of nodes whose children have not yet been generated (like the authors'
frontier
variable), and a dictionary named expanded
to keep
the nodes for which we have generated the children (like the authors'
explored
variable). In each a node is stored with its parent,
allowing a solution path to be generated be stepping backwards from
the goal node once it is found.
Given the start_state
, goal_state
, successors_f
, and
breadth_first
(a boolean variable):
expanded
to be an empty dictionaryun_expanded
to be a list containing the pair (start_state, None)
start_state
is the goal_state
, return the list containing just start_state
un_expanded
is not empty:un_expanded
a (state
, parent
) pair.children
of state
using the successors_f
function.tuple(state): parent
to the expanded
dictionarychildren
any states that are already in expanded
or un_expanded
. When looking for states in expanded
, remember to apply tuple
to them first.goal_state
is in children
):[state, goal_state]
.parent
exists:parent
to the front of the solution path.parent
to the parent of parent
.children
, so that we all find the same solution paths.children
list by changing each entry to be a pair (child
, parent
), where parent
is the parent of the child.children
list into the un_expanded
list at the front if doing breadth-first search, or at the back if doing depth-first search. Use the boolean variable breadth_first
provided as the last argument in the call to this function to control inserting at the front the back. Do this insertion with one statement, not a for loop, to preserve the order of the children.The line that starts with "For efficiency, remove from children any states ..." can be tricky, especially if you try to implement this with a for loop that steps through the elements of children
and remove them.
Here is an example of a result that might surprise you.
nums = [0, 6, 4, 5, 2, 3, 9, 7, 8]
nums
[0, 6, 4, 5, 2, 3, 9, 7, 8]
for num in nums:
print(num)
0 6 4 5 2 3 9 7 8
for num in nums:
if num < 5:
nums.remove(num)
nums
[6, 5, 3, 9, 7, 8]
Why is 3 still in this list?
This is because you are modifying the list nums
which is controlling the for loop. After 2 is removed, the for loop moves on to the value at 9. (Try printing the value of nums
at the end of each repetition to see this.)
How can we fix this? There are two ways. The first is to use a copy of the nums
list to control the for loop, so you are free to modify the original nums
list.
import copy
nums = [0, 6, 4, 5, 2, 3, 9, 7, 8]
for num in copy.copy(nums):
if num < 5:
nums.remove(num)
nums
[6, 5, 9, 7, 8]
A second way is to use a list comprehension. This ends up constructing a new list, keeping just the elements you want.
nums = [0, 6, 4, 5, 2, 3, 9, 7, 8]
nums = [num for num in nums if num >= 5]
nums
[6, 5, 9, 7, 8]